package com.example.compose.biometricauthentic.utils

import android.content.Context
import android.util.Log
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.example.compose.R
import com.example.compose.biometricauthentic.manager.CryptoManager
import java.util.UUID

object BiometricHelper {

    fun isBiometricAvailable(context: Context): Boolean {
        val biometricManager = BiometricManager.from(context)
        return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
            BiometricManager.BIOMETRIC_SUCCESS -> true
            else -> {
                Log.d("TAG", "Biometric authentication not available")
                false
            }
        }
    }

    private fun getBiometricPrompt(
        context: FragmentActivity,
        onAuthSucceed: (BiometricPrompt.AuthenticationResult) -> Unit
    ): BiometricPrompt {
        val biometricPrompt = BiometricPrompt(
            context,
            ContextCompat.getMainExecutor(context),
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    Log.d("TAG", "Authentication Succeeded: ${result.cryptoObject}")
                    onAuthSucceed(result)
                }

                override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                    Log.d("TAG", "onAuthenticationError: ")
                }

                override fun onAuthenticationFailed() {
                    Log.d("TAG", "onAuthenticationFailed: ")
                }
            }
        )
        return biometricPrompt
    }

    private fun getPromptInfo(context: FragmentActivity): BiometricPrompt.PromptInfo {
        return BiometricPrompt.PromptInfo.Builder()
            .setTitle(context.getString(R.string.bioTitle))
            .setSubtitle(context.getString(R.string.bioSubtitle))
            .setDescription(context.getString(R.string.bioDesc))
            .setConfirmationRequired(false)
            .setNegativeButtonText(context.getString(R.string.bioButtonText))
            .build()
    }

    fun registerUserBiometrics(
        context: FragmentActivity,
        onSuccess: (authResult: BiometricPrompt.AuthenticationResult) -> Unit = {}
    ) {
        val cryptoManager = CryptoManager()
        val cipher = cryptoManager.initEncryptionCipher(SECRET_KEY)
        val biometricPrompt = getBiometricPrompt(context) { authResult ->
            authResult.cryptoObject?.cipher?.let { cipher ->
                val token = UUID.randomUUID().toString()
                val encryptedToken = cryptoManager.encrypt(token, cipher)
                cryptoManager.saveToPrefs(
                    encryptedToken,
                    context,
                    ENCRYPTED_FILE_NAME,
                    Context.MODE_PRIVATE,
                    PREF_BIOMETRIC
                )
                onSuccess(authResult)
            }
        }
        biometricPrompt.authenticate(
            getPromptInfo(context = context),
            BiometricPrompt.CryptoObject(cipher)
        )
    }

    fun authenticateUser(
        context: FragmentActivity,
        onSuccess: (plainText: String) -> Unit
    ) {
        val cryptoManager = CryptoManager()
        val encryptedData = cryptoManager.getFromPrefs(
            context = context,
            ENCRYPTED_FILE_NAME,
            Context.MODE_PRIVATE,
            PREF_BIOMETRIC
        )
        encryptedData?.let { data ->
            val cipher = cryptoManager.initDecryptionCipher(SECRET_KEY, data.initializationVector)
            val biometricPrompt = getBiometricPrompt(context = context) { authResult ->
                authResult.cryptoObject?.cipher?.let { cipher ->
                    val plainText = cryptoManager.decrypt(data.ciphertext, cipher)
                    onSuccess(plainText)
                }
            }
            val promptInfo = getPromptInfo(context = context)
            biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
        }
    }
}